﻿namespace HZY.Framework.Repository.EntityFramework;

/// <summary>
/// EntityFrameworkRepositories 工具类
/// </summary>
public static class RepositoryEntityFrameworkExtensions
{
    /// <summary>
    /// 实体 和 dbContext 集合
    /// </summary>
    private static readonly List<DbContextAndEntityInfo> DbContextAndEntityInfos = new();

    /// <summary>
    /// 审计对象
    /// </summary>
    private static List<AuditOptions> _auditOptions = new List<AuditOptions>();

    /// <summary>
    /// 审计对象
    /// </summary>
    public static List<AuditOptions> AuditOptions => _auditOptions;

    static RepositoryEntityFrameworkExtensions()
    {
    }

    /// <summary>
    /// 缓存 dbContext 类型
    /// </summary>
    /// <param name="entityType"></param>
    /// <param name="dbContextType"></param>
    public static void AddDbContextAndEntityInfo(Type entityType, Type dbContextType)
    {
        //验证是否已经存在
        if (DbContextAndEntityInfos.Any(a =>
                a.EntityType.FullName == entityType.FullName && a.DbContextType.FullName == dbContextType.FullName))
            return;

        DbContextAndEntityInfos.Add(new DbContextAndEntityInfo(entityType, dbContextType));
    }

    /// <summary>
    /// 获取缓存的 dbContext 类型
    /// </summary>
    /// <returns></returns>
    public static List<DbContextAndEntityInfo> GetDbContextAndEntityInfoAll() => DbContextAndEntityInfos;

    /// <summary>
    /// 获取所有的 dbContext
    /// </summary>
    /// <returns></returns>
    public static List<Type> GetDbContextTypeAll() =>
        DbContextAndEntityInfos.Select(s => s.DbContextType).Distinct().ToList();

    #region EntityFrameworkRepositories

    /// <summary>
    /// 添加监控 EntityFramework
    /// </summary>
    /// <param name="dbContextOptionsBuilder"></param>
    /// <param name="isMonitor"></param>
    /// <returns></returns>
    public static DbContextOptionsBuilder AddEntityFrameworkMonitor(
    this DbContextOptionsBuilder dbContextOptionsBuilder, bool isMonitor = false)
    {
        dbContextOptionsBuilder.AddInterceptors(new ShardingDbCommandInterceptor());

        //注册监控程序
        if (isMonitor)
        {
            dbContextOptionsBuilder.AddInterceptors(new MonitorDbConnectionInterceptor());
            dbContextOptionsBuilder.AddInterceptors(new MonitorDbCommandInterceptor());
            dbContextOptionsBuilder.AddInterceptors(new MonitorDbTransactionInterceptor());
        }

        return dbContextOptionsBuilder;
    }

    /// <summary>
    /// 注册 EntityFrameworkRepositories
    /// 主要用于注册动态仓储
    /// </summary>
    /// <param name="services"></param>
    /// <param name="dbContextType"></param>
    /// <param name="auditOptionsCallBack">审计信息配置</param>
    /// <param name="serviceLifetime"></param>
    /// <returns></returns>
    public static IServiceCollection AddEntityFrameworkRepositories(
        this IServiceCollection services,
        Type dbContextType,
        Action<List<AuditOptions>>? auditOptionsCallBack = null,
        ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)
    {
        List<AuditOptions> auditOptions = new List<AuditOptions>();

        #region 添加系统默认标准审计字段

        // 审计字段 - 标准版本
        auditOptions!.Add(new AuditOptions()
        {
            CreationTimeFieldName = nameof(ICreateEntity.CreationTime),
            CreatorUserIdFieldName = nameof(ICreateEntity.CreatorUserId),
            LastModificationTimeFieldName = nameof(IUpdateEntity.LastModificationTime),
            LastModifierUserIdFieldName = nameof(IUpdateEntity.LastModifierUserId),
            DeleterUserIdFieldName = nameof(IDeleteEntity.DeleterUserId),
            DeletionTimeFieldName = nameof(IDeleteEntity.DeletionTime),
            IsDeletedFieldName = nameof(IDeleteEntity.IsDeleted),
        });
        // 审计字段 - 标准版本v2
        auditOptions.Add(new AuditOptions()
        {
            CreationTimeFieldName = nameof(ICreateEntityV2.CreateTime),
            CreatorUserIdFieldName = nameof(ICreateEntityV2.CreateBy),
            LastModificationTimeFieldName = nameof(IUpdateEntityV2.UpdateTime),
            LastModifierUserIdFieldName = nameof(IUpdateEntityV2.UpdateBy),
            DeletionTimeFieldName = nameof(IDeleteEntityV2.DeleteTime),
            DeleterUserIdFieldName = nameof(IDeleteEntityV2.DeleteBy),
            IsDeletedFieldName = nameof(IDeleteEntityV2.DeleteFlag),
        });

        auditOptionsCallBack?.Invoke(auditOptions);
        _auditOptions.AddRange(auditOptions);
        #endregion

        var dbContextConfigAttribute = dbContextType.GetCustomAttribute<DbContextConfigAttribute>()!;

        UseEntityFrameworkRepositories(dbContextType);

        // 如果上下文没有配置 DbContextConfigAttribute 则使用默认的扫描方式 DbSet<> 注册
        if (dbContextConfigAttribute != null)
        {
            // 将实体存储起来
            var modelTypes = dbContextConfigAttribute.GetModelTypes(dbContextType);
            foreach (var item in modelTypes)
            {
                AddDbContextAndEntityInfo(item, dbContextType);
            }
        }

        // 将实体与仓储接口注入到 ioc
        GetDbContextAndEntityInfoAll()
            .Where(w => w.DbContextType.FullName == dbContextType.FullName)
            .ToList()
            .ForEach(item =>
            {
                var interfaceType = typeof(IRepository<>).MakeGenericType(item.EntityType);
                var implType = typeof(RepositoryImpl<,>).MakeGenericType(item.EntityType, dbContextType);

                switch (serviceLifetime)
                {
                    case ServiceLifetime.Transient:
                        services.AddTransient(interfaceType, implType);
                        break;
                    case ServiceLifetime.Singleton:
                        services.AddSingleton(interfaceType, implType);
                        break;
                    default:
                        services.AddScoped(interfaceType, implType);
                        break;
                }
            });

        return services;
    }

    /// <summary>
    /// 使用 EntityFrameworkRepositories
    /// </summary>
    /// <param name="dbContextTypes">数据上下文类型</param>
    /// <returns></returns>
    private static void UseEntityFrameworkRepositories(params Type[] dbContextTypes)
    {
        foreach (var item in dbContextTypes)
        {
            //扫描类型下面的 dbSet model
            var propertyInfos =
                item.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            var dbSets = propertyInfos.Where(w => w.PropertyType.Name == "DbSet`1");

            foreach (var dbSet in dbSets)
            {
                if (dbSet.PropertyType.GenericTypeArguments.Length <= 0) continue;

                var model = dbSet.PropertyType.GenericTypeArguments[0];
                AddDbContextAndEntityInfo(model, item);
            }
        }
    }

    #endregion
}